/*
    日志器类模块
    功能：对前面所有的模块进行整合，向外提供接口完成不同等级日志的输出
    类成员：
        1.格式化模块对象
        2.落地模块对象数组 （一个日志器可能会向多个位置进行日志输出）
        3.默认的日志输出限制等级  （大于等于限制日志等级的才能输出）
        4.互斥锁   （保证日志输出过程线程安全，不会出现交叉日志）
        5.日志器名称  （日志器的唯一标识，以便于查找）
    提供操纵：
        debug等级日志输出操作（分别会封装日志消息LogMsg -- 各个接口日志等级不同）
        info等级日志输出操作
        warn等级日志输出操作
        error等级日志输出操作
        fatal等级日志输出操作
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
#include"util.hpp"
#include"message.hpp"
#include"Level.hpp"
#include"format.hpp"
#include"LogSink.hpp"
#include "Looper.hpp"
#include<atomic>
#include<mutex>
#include<cstdarg>
#include<unordered_map>

namespace Hxy_Log
{
    //抽象Logger基类（派生同步日志器&异步日志器）
    class Logger
    {
        public:
        Logger(const std::string& name,
               const LogLevel::value& value,
               const Formatter::ptr& fmt,
               const std::vector<LogSink::ptr>& sink)
               :_name_logger(name),
                _val_logger(value),
                _fmt_logger(fmt),
                _sink_loggers(sink.begin(),sink.end()){}
        using ptr = std::shared_ptr<Logger>;
        const std::string& GetName(){return _name_logger;}
        //完成构造日志信息对象过程并格式化输出，得到格式化后的日志信息字符串--然后进行日志落地输出
        void debug(const std::string& filename,const size_t& line,const std::string& fmt,...)
        {   //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1.判断传入的日志等级是否大于等于当前的日志等级
            if(LogLevel::value::DEBUG < _val_logger) return;
            //2.通过对传入的fmt参数和不定参进行数据分析，提取出日志消息字符串
            va_list va;    //获取fmt函数指针
            va_start(va,fmt);  
            char* str;  //存放不定参参数信息
            int ret = vasprintf(&str,fmt.c_str(),va);  //将fmt与后面的不定参进行格式化放进str
            if(ret == -1) {
                std::cout<<"vasprintf failure!!\n";
                return;
            }
            va_end(va); //指针置空
            serializable(LogLevel::value::DEBUG,filename,line,str);
            free(str);  //!!!vasprintf会对str进行开辟空间，必须进行释放不然会造成内存泄漏
        }
        void info(const std::string& filename,const size_t& line,const std::string& fmt,...)
        {
            //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1.判断传入的日志等级是否大于等于当前的日志等级
            if(LogLevel::value::INFO < _val_logger) return;
            //2.通过对传入的fmt参数和不定参进行数据分析，提取出日志消息字符串
            va_list va;    //获取fmt函数指针
            va_start(va,fmt);  
            char* str;  //存放不定参参数信息
            int ret = vasprintf(&str,fmt.c_str(),va);  //将fmt与后面的不定参进行格式化放进str
            if(ret == -1) {
                std::cout<<"vasprintf failure!!\n";
                return;
            }
            va_end(va); //指针置空
            serializable(LogLevel::value::INFO,filename,line,str);
            free(str);  //!!!vasprintf会对str进行开辟空间，必须进行释放不然会造成内存泄漏
        }
        void warn(const std::string& filename,const size_t& line,const std::string& fmt,...)
        {
            //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1.判断传入的日志等级是否大于等于当前的日志等级
            if(LogLevel::value::WARN < _val_logger) return;
            //2.通过对传入的fmt参数和不定参进行数据分析，提取出日志消息字符串
            va_list va;    //获取fmt函数指针
            va_start(va,fmt);  
            char* str;  //存放不定参参数信息
            int ret = vasprintf(&str,fmt.c_str(),va);  //将fmt与后面的不定参进行格式化放进str
            if(ret == -1) {
                std::cout<<"vasprintf failure!!\n";
                return;
            }
            va_end(va); //指针置空
            serializable(LogLevel::value::WARN,filename,line,str);
            free(str);  //!!!vasprintf会对str进行开辟空间，必须进行释放不然会造成内存泄漏
        }
        void error(const std::string& filename,const size_t& line,const std::string& fmt,...)
        {
            //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1.判断传入的日志等级是否大于等于当前的日志等级
            if(LogLevel::value::ERROR < _val_logger) return;
            //2.通过对传入的fmt参数和不定参进行数据分析，提取出日志消息字符串
            va_list va;    //获取fmt函数指针
            va_start(va,fmt);  
            char* str;  //存放不定参参数信息
            int ret = vasprintf(&str,fmt.c_str(),va);  //将fmt与后面的不定参进行格式化放进str
            if(ret == -1) {
                std::cout<<"vasprintf failure!!\n";
                return;
            }
            va_end(va); //指针置空
            serializable(LogLevel::value::ERROR,filename,line,str);
            free(str);  //!!!vasprintf会对str进行开辟空间，必须进行释放不然会造成内存泄漏
        }
        void fatal(const std::string& filename,const size_t& line,const std::string& fmt,...)
        {
            //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            //1.判断传入的日志等级是否大于等于当前的日志等级
            if(LogLevel::value::FATAL < _val_logger) return;
            //2.通过对传入的fmt参数和不定参进行数据分析，提取出日志消息字符串
            va_list va;    //获取fmt函数指针
            va_start(va,fmt);  
            char* str;  //存放不定参参数信息
            int ret = vasprintf(&str,fmt.c_str(),va);  //将fmt与后面的不定参进行格式化放进str
            if(ret == -1) {
                std::cout<<"vasprintf failure!!\n";
                return;
            }
            va_end(va); //指针置空
            serializable(LogLevel::value::FATAL,filename,line,str);
            free(str);  //!!!vasprintf会对str进行开辟空间，必须进行释放不然会造成内存泄漏
        }
         
        protected:
        void serializable(const LogLevel::value& val,const std::string filename,const size_t& line,const char* str)
        {
            //3.构造LogMsg对象
            LogMsg msg(val,filename,line,str,_name_logger);
            //4.通过格式化工具对LogMsg对象进行格式化，最终得到格式化字符串
            // std::stringstream ss;
            // _fmt_logger->format(ss,msg);
            std::string fmt_str = _fmt_logger->format(msg);
            //5.进行落地
            log(fmt_str.c_str(),fmt_str.size());
        }
        //抽象日志器落地方式--完成实际的日志落地输出，不同的日志器会有不同的落地方法
        virtual void log(const char* data,size_t len) = 0;

        protected:
        std::string _name_logger;   //日志器名称
        std::atomic<LogLevel::value> _val_logger;   //日志器默认等级 -- 每次都会进行调用判断，设置成原子的不会出现死锁问题
        Formatter::ptr _fmt_logger;  //格式化模块对象
        std::mutex _mtx_logger; //互斥锁
        std::vector<LogSink::ptr> _sink_loggers;  //落地模块数组
    };

    //派生同步日志器
    class SyncLogger : public Logger
    {
        public:
         SyncLogger(const std::string& name,
               const LogLevel::value& value,
               const Formatter::ptr& fmt,
               const std::vector<LogSink::ptr>& sink)
               :Logger(name,value,fmt,sink){}
        protected:
            //同步日志器落地，是直接通过落地模块句柄进行日志落地
            virtual void log(const char* data,size_t len)
            {
                //每个日志落地都需要保持线程安全
                std::unique_lock<std::mutex> mtx(_mtx_logger); // 会进行加解锁
                if(_sink_loggers.size() == 0) return;
                for(const auto& sink : _sink_loggers)
                {
                    sink->log(data,len);
                }
            }
    };
    //派生异步日志器
    class AsyncLogger : public Logger
    {
        public:
            AsyncLogger(const std::string& name,
               const LogLevel::value& value,
               const Formatter::ptr& fmt,
               const std::vector<LogSink::ptr>& sink,
               const LoopType& type)
               :Logger(name,value,fmt,sink),
               _loop(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::_reallog,this,std::placeholders::_1),type)){}
            virtual void log(const char* data,size_t len)
            {   
                _loop->push(data,len);  //push操作里面是线程安全的 这里不需要加锁
            }
        private:
            void _reallog(Buffer& buf)    //实际落地方向--回调函数
            {
                if(_sink_loggers.size() == 0) return;
                for(const auto& sink : _sink_loggers)
                {
                    sink->log(buf.begin(),buf.readableSize());  //不需要加锁 -- 因为处理消费者线程只有一个
                }
            }
        private:
            AsyncLooper::ptr _loop;
    };

    enum class LoggerType
    {
        SYNCTYPE,
        ASYNCTYPE
    };
    //使用建造者模式来建造日志器，而不要让用户直接去构造日志器，简化用户的使用复杂度
    //1.抽象一个日志器建造者类（完成日志对象所需零部件的构造&日志器构造）
    //  1.设置日志器类型
    //  2.将不同类型日志器的创建放到同一个日志器建造者类中完成
    class LoggerBuilder
    {
        public:
            LoggerBuilder()
            :_lgtype(LoggerType::SYNCTYPE),
             _limte_level(LogLevel::value::DEBUG),
             _lptype(LoopType::ASYNC_SAFE){}
            void BuildLoggerType(LoggerType type){_lgtype = type;}
            void BuildLoggerName(std::string name){_name_logger = name;}
            void BuildLoopTypeUnSafe(){_lptype = LoopType::ASYNC_UNSAFE;}
            void BuildLoggerValue(LogLevel::value value){_limte_level = value;}
            void BuildFormatter(const std::string& fmt = ""){
                if(fmt.size()!=0)_fmt_logger = std::make_shared<Formatter>(fmt);
                else _fmt_logger = nullptr;
            }
            template<typename SINKTYPE,typename ...Args>
            void BuildSink(Args && ...args)
            {
                LogSink::ptr lsp = std::make_shared<SINKTYPE>(std::forward<Args>(args)...);
                _sink_loggers.push_back(lsp);
            }
            virtual Logger::ptr build() = 0;
        protected:
        LoopType _lptype;
        LoggerType _lgtype;
        std::string _name_logger;   //日志器名称
        LogLevel::value _limte_level;   //日志器默认等级 -- 每次都会进行调用判断，设置成原子的不会出现死锁问题
        Formatter::ptr _fmt_logger;  //格式化模块对象
        std::vector<LogSink::ptr> _sink_loggers;  //落地模块数组
    };
    //2.派生出具体的建造者类---局部日志器的建造者&全局的日志器建造者（后面添加了单例模式管理器后，将日志添加到全局）
    class LocalLoggerBuilder : public LoggerBuilder
    {
        public:
        virtual Logger::ptr build() override
        {
            assert(!_name_logger.empty());
            if(_fmt_logger.get() == nullptr){
                _fmt_logger = std::make_shared<Formatter>();
            }
            if(_sink_loggers.size() == 0){
                BuildSink<StdOutSink>();
            }
            if(_lgtype == LoggerType::ASYNCTYPE)
            {
                return std::make_shared<AsyncLogger>(_name_logger,_limte_level,_fmt_logger,_sink_loggers,_lptype);
            }

            return std::make_shared<SyncLogger>(_name_logger,_limte_level,_fmt_logger,_sink_loggers);
        }
    };

    //日志器管理
    class LoggerManager
    {
        public:
        static LoggerManager& GetInstance()  //单例模式
        {
            //在c++11之后，函数内的静态成员是线程安全的，在创建阶段还没完成多个线程访问会进行阻塞
            static LoggerManager lmg;
            return lmg;
        }
        void addLogger(Logger::ptr& logger) //增加日志器
        {
            if(hasLogger(logger->GetName())) return;
            std::unique_lock<std::mutex>mtx(_mutex);
            _loggers.insert(std::make_pair(logger->GetName(),logger));
        }
        bool hasLogger(const std::string& name)  //判断是否在日志器内
        {
            std::unique_lock<std::mutex>mtx(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return false;
            else return true;
        }
        Logger::ptr getLogger(const std::string& name) //获得默认日志器
        {
            std::unique_lock<std::mutex>mtx(_mutex);
            if(_loggers.count(name)) return _loggers[name];
            else return Logger::ptr();
        }
        Logger::ptr rootLogger()    //返回默认日志器
        {
            std::unique_lock<std::mutex>mtx(_mutex);
            return _root_logger;
        }
        private:
        LoggerManager(){    //构建默认日志器
            std::unique_ptr<LoggerBuilder> lgb (new LocalLoggerBuilder());
            lgb->BuildLoggerName("root");
            _root_logger = lgb->build();
            _loggers.insert(std::make_pair("root",_root_logger));
        }
        private:
        std::mutex _mutex;  //互斥锁
        Logger::ptr _root_logger;  //默认日志器
        std::unordered_map<std::string,Logger::ptr> _loggers;
    };

    //设置一个全局建造者模式--能够自动讲建造的对象添加到日志器管理器当中
    class GlobalLoggerBuilder : public LoggerBuilder
    {
        public:
        virtual Logger::ptr build() override
        {
            assert(!_name_logger.empty());
            if(_fmt_logger.get() == nullptr){
                _fmt_logger = std::make_shared<Formatter>();
            }
            if(_sink_loggers.size() == 0){
                BuildSink<StdOutSink>();
            }
            Logger::ptr logger;
            if(_lgtype == LoggerType::ASYNCTYPE)
            {
                logger = std::make_shared<AsyncLogger>(_name_logger,_limte_level,_fmt_logger,_sink_loggers,_lptype);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_name_logger,_limte_level,_fmt_logger,_sink_loggers);
            }
                LoggerManager::GetInstance().addLogger(logger);
            return logger;
        }
    };

}

#endif